// @ts-check
import React, { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
import styles from './style.less'
import {
  usePlayerStateSelector, usePlayerStoreDispatch,
  changePlayState, saveAudioInstance, PlayerStoreContext, replacePlayList
} from '@/components/Player/store'
import { LockOutlined, PauseOutlined, PlayCircleOutlined, SoundOutlined, StepBackwardOutlined, StepForwardOutlined, UnlockOutlined, LoadingOutlined, CustomerServiceFilled, MinusOutlined, FilterOutlined, MenuUnfoldOutlined, UpOutlined, DownOutlined } from '@ant-design/icons'
import cs from 'classnames'
import { PlayState } from './enums'
import { parseTime } from './utils'
import useRequest from './api'
import Playlist from './components/playlist'
import PlayerEvent from '@/components/Player/events'
import Progress from './components/progress'
import Lyric from './components/lyric'
import Visualization from './components/visualization'

let timerId
/**
 * 播放器组件
 * @param {import('./types').PlayerProps} props
 */
const Player = (props) => {
  /** @type {  import('react').MutableRefObject<HTMLAudioElement> } */
  const audioRef = useRef()
  const dispatch = usePlayerStoreDispatch()
  const playerState = usePlayerStateSelector((state) => ({
    musicId: state.musicId,
    playState: state.playState,
    playList: state.playList
  }))

  const [detailHeight, setDetailHeight] = useState(0) 
  const detailRef = useCallback((/** @type {HTMLDivElement} */ref) => {
    if(ref) {
      setDetailHeight(ref.offsetHeight)
    }
  }, [])
  const [isPlaying, setIsPlaying] = useState(false)
  const [isLocked, setIsLocked] = useState(false)
  const [playerShow, setPlayerShow] = useState(true)
  const [fullScreen, setFullScreen] = useState(false)
  // 歌曲地址
  const [playURL, getPlayURL] = useRequest('/song/url')
  // 歌曲详情
  const [musicDetaiReq, getMusicDetai] = useRequest('/song/detail')

  // 音频时长
  const [duration, setDuration] = useState('00:00')
  // 当前播放时间
  const [currentTime, setCurrentTime] = useState(0)
  // 播放进度
  const [playProgress, setPlayProgress] = useState(0)
  // 音量
  const [volume, setVolume] = useState(0)
  const [lastVolume, setLastVolume] = useState(0)

  // 点击歌曲列表图标
  const [showPlaylist, setShowPlaylist] = useState(false)
  const onClickPlaylistIcon = useCallback(() => {
    setShowPlaylist(!showPlaylist)
  }, [showPlaylist])

  // 原生事件
  useLayoutEffect(() => {
    dispatch(saveAudioInstance(audioRef.current))
    setVolume(audioRef.current.volume)
    setLastVolume(audioRef.current.volume)

    audioRef.current.addEventListener('pause', () => {
      setIsPlaying(false)
      dispatch(changePlayState(PlayState.pause))
      PlayerEvent.triggerEvent('pause')
    })
    audioRef.current.addEventListener('timeupdate', () => {
      // 更新时间
      const { current:{
        currentTime,
        duration
      }} = audioRef
      setCurrentTime(currentTime)
      setPlayProgress(currentTime * 100 / duration)
    })

    audioRef.current.addEventListener('loadedmetadata', () => {
      // 设置暂停图标
      setIsPlaying(false)
    })
    audioRef.current.addEventListener('canplay', () => {
      // 可以播放
      PlayerEvent.triggerEvent('prepared')
    })
    audioRef.current.addEventListener('playing', () => {
      // 成功开始播放
      const { current:{
        duration
      }} = audioRef
      dispatch(changePlayState(PlayState.play))
      setIsPlaying(true)
      setDuration(parseTime(duration))
      PlayerEvent.triggerEvent('play')
    })
    audioRef.current.addEventListener('ended', () => {
      // 播放完成
      setIsPlaying(false)
      dispatch(changePlayState(PlayState.end))
      PlayerEvent.triggerEvent('end')
    })

    // audioRef.current.addEventListener('volumechange', () => {
    //    console.log('volume:', audioRef.current.volume)
    // })
  }, [])

  // 触发事件
  const onPlayBtnClick = useCallback(() => {
    if(!playerState.musicId) return
    setIsPlaying(!isPlaying)
    if(isPlaying) {
      audioRef.current.pause()

    }else {
      audioRef.current.play()
    }
  }, [isPlaying, playerState.musicId])

  const onNextBtnClick = useCallback(() => {
    PlayerEvent.triggerEvent('next')
  }, [])

  const onPrevBtnClick = useCallback(() => {
    PlayerEvent.triggerEvent('prev')
  }, [])

  // 锁定隐藏
  const onLockClick = useCallback(() => {
    setIsLocked(!isLocked)
  }, [isLocked])

  // 鼠标移出隐藏
  const onPlayerMouseOver = useCallback(() => {
    if(!playerShow) {
      setPlayerShow(true)
    }
    clearTimeout(timerId)
  }, [playerShow])

  const onPlayerMouseLeave = useCallback(() => {
    timerId = setTimeout(() => {
      if(playerShow) {
        if(showPlaylist) return
        if(!isLocked) {
          setPlayerShow(false)
        }
      }
    }, 2000)
  }, [playerShow, isLocked, showPlaylist])

  // 切换歌曲
  useEffect(() => {
    if(playerState.musicId) {
      const { musicId } = playerState
      getPlayURL({
        id: musicId
      })
      getMusicDetai({
        ids: musicId
      })
    }
  }, [playerState.musicId])

  // 调整音量
  const onVolumeBarClickDown = useCallback((e) => {
    const  {
      height,
      top
    } = e.currentTarget.getBoundingClientRect()
    const distance = e.nativeEvent.clientY - top
    setVolume(1 - distance / height)
    e.currentTarget.onmousemove = (e) => {
      const distance = e.clientY - top
      setVolume(1 - distance / height)
    }
  }, [])
  const onVolumeBarClickUp = useCallback((e) =>{
    e.currentTarget.onmousemove = null
  }, [])
  const onVolumeDotLeave = useCallback((e) =>{
    e.currentTarget.onmousemove = null
  }, [])
  const onVolumeIconClick = useCallback(() => {
    setLastVolume(volume)
    const newVol = volume == 0 ? lastVolume : 0

    setVolume(newVol)
  }, [volume, lastVolume])
  
  useEffect(() => {
    if(audioRef.current) {
      const newVol = volume < 0 ? 0 : volume > 1 ? 1 : volume
      audioRef.current.volume = newVol
    }
  }, [volume])

  // 播放列表为空时隐藏
  useEffect(() => {
    if(playerState.playList.length <= 0) {
      setShowPlaylist(false)
    }
  }, [playerState.playList, playerShow])

  // 渲染播放列表顶部
  const renderToolBar = useCallback(() => {
    return <div className = {styles['topBarContainer']}>
      <div>当前播放</div>
      <div>
        <div className = { styles['total'] }>总{playerState.playList.length}首</div>
        <div className = { styles['clear'] } onClick={() => dispatch(replacePlayList([]))}>清空列表</div>
      </div>
    </div>
  }, [playerState.playList])

  // 进度条
  const onSetProgress = useCallback((progress) => {
    // 设置进度
    if(audioRef.current) {
      if(audioRef.current.src) {
        const newTime = audioRef.current.duration * progress
        if(!isNaN(newTime)) {
          audioRef.current.currentTime = newTime
        }
      }
    }
  }, [])
  const onDragProgress = useCallback((progress, dragEnd) =>{
    // 拖动进度条
    if(audioRef.current) {
      if(audioRef.current.src) {
        audioRef.current.pause()
        setCurrentTime(audioRef.current.duration * progress)
        setPlayProgress(progress * 100)
        if(dragEnd) {
          const newTime = audioRef.current.duration * progress
          if(!isNaN(newTime)) {
            audioRef.current.currentTime = newTime
            audioRef.current.pause()
          }
        }
      }
    }
  }, [])

  // 展开全屏
  const onClickExpendIcon = useCallback(() => {
    setFullScreen(!fullScreen)
  }, [fullScreen])
  return (
    <>
      <div
        className = {styles['player']}
        style = { {transform: `translate(0%, ${playerShow ? 0 : 95 }%)`} }
        onMouseLeave = { onPlayerMouseLeave }
        onMouseOver = { onPlayerMouseOver }>
        <div className={styles['lock']} title="锁定" onClick = { onLockClick }>
          {
            isLocked ? <LockOutlined /> : <UnlockOutlined />
          }
        </div>
        {/* 歌曲信息 */}
        <div className = {cs(styles['musicInfo'], {[styles['musicInfoHidden']]: playerState.playList.length <= 0})}>
          <div 
            className = {cs(styles['infoControl'], {[styles['infoControlChange']]: fullScreen ? true : false})}>
            <div className = {styles['fullScreen']}>
              <div className = {styles['downIcon']} onClick = { onClickExpendIcon } title = "收起详情">
                <DownOutlined />
              </div>
            </div>
            <div className = {styles['normal']}>
              <div className = {cs(styles['mask'], {[styles['maskHover']]: !musicDetaiReq.loading})}>
                {/* 全屏按钮 */}
                <div 
                  title = "展开详情"
                  className = {styles['expend']} 
                  onClick = { onClickExpendIcon }>
                  <div className = {styles['blurLayer']}></div>
                  <div className = {styles['content']}>
                    <UpOutlined />
                  </div>
                </div>
                {/* 加载器 */}
                <div className = {styles['loading']} style = {{visibility: musicDetaiReq.loading ? 'visible' : 'hidden'}}>
                  <LoadingOutlined spin/>
                </div>
                {
                  musicDetaiReq.data?.songs[0].al.picUrl ?
                    <img src={musicDetaiReq.data?.songs[0].al.picUrl} alt="专辑图片"/> :
                    <div className = {styles['placeholder']}>
                      <CustomerServiceFilled />
                    </div>
                }
              </div>
              <div className = {styles['text']}>
                <div className = {styles['musicName']}>{musicDetaiReq.data?.songs[0].name}</div>
                <div className = {styles['singer']}>{musicDetaiReq.data?.songs[0].ar[0].name}</div>
              </div>
            </div>
          </div>
        </div>
        
        {/* 中间 */}
        <div className = {styles['controller']}>
          <div className ={styles['control']}>
            <div className = {styles['prevBtn']} onClick = { onPrevBtnClick }>
              <StepBackwardOutlined />
            </div>
            <div className = {styles['playBtn']} onClick = { onPlayBtnClick }>
              {
                isPlaying ? <PauseOutlined /> : <PlayCircleOutlined />
              }
            </div>
            <div className = {styles['nextBtn']} onClick = { onNextBtnClick }>
              <StepForwardOutlined />
            </div>
          </div>
          {/* 进度条 */}
          <Progress
            currentTime = { currentTime }
            playProgress = { playProgress }
            duration = { duration }
            onSetProgress = { onSetProgress }
            onDragProgress = { onDragProgress }
          />
        </div>
        
        {/* 其他操作 */}
        <div className = {styles['otherOperator']}>
          <div className={cs(styles['opBtn'], styles['sound'])}>
            <div
              className = {styles['soundIconContainer']}
              onMouseDown = {onVolumeIconClick}
            >
              <FilterOutlined
                className = {styles['soundIcon']}/>
              <MinusOutlined className = {styles['mute']} style = {{ visibility: volume > 0 ? 'hidden' : 'visible'}}/>
            </div>
            <div className = {styles['bufferArea']}>
              <div className = {styles['volumeBar']}>
                <div className = {styles['barOuter']}
                  onMouseLeave = {onVolumeDotLeave}
                  onMouseUp = {onVolumeBarClickUp}
                  onMouseDown = { onVolumeBarClickDown }>
                  <div className = {styles['dot']}></div>
                  <div
                    className = {styles['barInner']}
                    style = {{height: `${volume * 100}%`}}>
                  </div>
                </div>
              </div>
            </div>
          </div>
        
          <div className={cs(styles['opBtn'], styles['playList'])}>
            <div className = {styles['iconContainer']} onClick = { onClickPlaylistIcon }>
              <MenuUnfoldOutlined />
            </div>
          </div>
        </div>
        
        {/* 播放列表 */}
        <Playlist
          playingTextColor = { '#66cc66' } 
          renderToolBar = { renderToolBar }
          itemContainerClassName = {styles['playlistItemContainerClass']}
          itemClassName = {styles['playlistItemClass']}
          indicatorClassName = {styles['indicatorClass']}
          containerClassName = { cs(styles['playlistContainer'], {[styles['playlistContainerShow']]: showPlaylist}) }/>

      </div>
      {/* 详情 */}
      <div className = { cs(styles['detailLayer'], {[styles['detailLayerShow']]: fullScreen ? true : false}) }>
        <div className = { styles['topBar'] }>
          <div>
            <div className = {styles['downIcon'] } onClick = { onClickExpendIcon } title = "收起详情">
              <DownOutlined />
            </div>
          </div>
          <div>
            <div className = {styles['musicInfo']}>
              <div>{musicDetaiReq.data?.songs[0].name}</div>
              <div>{musicDetaiReq.data?.songs[0].ar[0].name}</div>
            </div>
          </div>
          <div></div>
        </div>
        <div className = { styles['content'] }>
          <div className = {styles['visualizationContainer']}>
            <Visualization 
              musicDetail = { musicDetaiReq.data }
              containerClassName = {styles['visualization']}/>
          </div>
          <div ref = { ref => detailRef(ref) } className = { styles['lrcContainer'] }>
            <Lyric
              style = {{height: `${detailHeight}px`}}
              currentTime = { currentTime }
              musicId = {playerState.musicId}
              offset = { playURL?.data?.data[0]?.freeTrialInfo?.start }
              containerClassName = {styles['lyric']}/>
          </div>
          <div className = {styles['other']}>

          </div>
        </div>
              
        {/* 虚化背景 */}
        <div className = {styles['blurBg']}>
          {
            musicDetaiReq.data?.songs[0]?.al?.picUrl ?
              <div style={{backgroundImage: `url(${musicDetaiReq.data?.songs[0]?.al?.picUrl})`}}></div> : null
              // <img src={musicDetaiReq.data?.songs[0]?.al?.picUrl} width="100%" height="100%"/> : null
          }
        </div>
      </div>
      
      <audio
        ref = { audioRef }
        className = {styles['audio']}
        autoPlay = {false}
        src={playURL?.data?.data[0].url}
        // crossOrigin = 'anonymous'
        // src={`https://music.163.com/song/media/outer/url?id=${playerState.musicId}.mp3`}
      >
      </audio>
    </>
  )
}

export default Player
